Découvrez Temporal.Duration de JavaScript, l'API moderne pour l'arithmétique, la comparaison et le formatage précis des intervalles de temps. Gérez les durées de manière globale en toute confiance, en évitant les pièges des objets Date.
Temporal.Duration en JavaScript : Maîtriser l'arithmétique et le formatage des intervalles de temps pour les applications mondiales
La gestion du temps dans le développement logiciel est notoirement complexe. Du suivi des calendriers de projet à travers les continents à la planification de visioconférences internationales, les nuances des intervalles de temps, des fuseaux horaires et de l'heure d'été peuvent rapidement conduire à des bogues subtils mais critiques. Pendant des décennies, les développeurs JavaScript se sont débattus avec l'objet Date intégré, un outil qui, bien que fonctionnel pour de simples points dans le temps, est insuffisant lorsqu'il s'agit d'arithmétique précise sur les intervalles de temps et d'une gestion du temps robuste et adaptée au contexte mondial.
Voici l'API Temporal de JavaScript – une proposition révolutionnaire conçue pour fournir une API moderne, robuste et conviviale pour travailler avec les dates et les heures en JavaScript. Parmi ses nouveaux types puissants, Temporal.Duration se distingue comme la solution définitive pour la gestion des intervalles de temps. Cet article vous propose une plongée en profondeur dans Temporal.Duration, en explorant ses capacités d'arithmétique, de comparaison et de formatage intelligent, afin que vos applications puissent gérer le temps avec une précision et une clarté mondiales.
Que vous construisiez un système logistique mondial, une plateforme de trading financier ou un planificateur d'événements multi-fuseaux horaires, la compréhension de Temporal.Duration est cruciale pour éliminer les ambiguïtés liées au temps et offrir des expériences utilisateur fiables et internationalisées.
Les insuffisances de l'objet Date de JavaScript pour les intervalles de temps
Avant de célébrer l'arrivée de Temporal.Duration, il est essentiel de comprendre les limites de l'objet Date existant, en particulier lorsqu'il s'agit d'intervalles de temps. L'objet Date représente un point spécifique dans le temps, mesuré en millisecondes depuis l'époque Unix (1er janvier 1970, UTC). Bien qu'il puisse être utilisé pour effectuer des calculs de base, il présente plusieurs défauts inhérents qui le rendent inadapté à une gestion robuste des durées :
-
Mutabilité : Les objets
Datesont mutables. Toute opération sur un objetDatemodifie son état interne, ce qui peut entraîner des effets de bord inattendus et des bogues difficiles à tracer, en particulier dans des applications complexes ou des environnements concurrents.const d = new Date('2023-01-15T10:00:00Z'); const d2 = d; // d2 référence maintenant le même objet que d d.setHours(d.getHours() + 1); console.log(d.toISOString()); // 2023-01-15T11:00:00.000Z console.log(d2.toISOString()); // 2023-01-15T11:00:00.000Z (d2 a aussi changé !) -
Absence de concept de durée : L'objet
Daten'a pas de concept direct de "durée" ou de "période". Le calcul de la différence entre deux dates donne un nombre de millisecondes, qui doit ensuite être converti manuellement en années, mois, jours, etc. Cette conversion manuelle est sujette aux erreurs, en particulier lorsqu'il s'agit de mois de longueur variable ou d'années bissextiles.const date1 = new Date('2023-01-01T00:00:00Z'); const date2 = new Date('2023-03-01T00:00:00Z'); const diffMs = date2.getTime() - date1.getTime(); // Combien de mois cela représente-t-il ? Et les années bissextiles ? // (diffMs / (1000 * 60 * 60 * 24 * 30)) n'est au mieux qu'une approximation. console.log(`Différence en millisecondes : ${diffMs}`); console.log(`Jours approximatifs : ${diffMs / (1000 * 60 * 60 * 24)}`); // Fonctionne pour les jours, mais n'est pas robuste pour les mois/années -
Ambigüité des fuseaux horaires : Les objets
Dateconfondent souvent l'heure locale et l'UTC. Bien qu'ils stockent en interne les millisecondes UTC, leurs méthodes fonctionnent fréquemment sur le fuseau horaire local du système par défaut, ce qui entraîne confusion et incohérences lorsque l'on travaille avec des systèmes distribués ou des utilisateurs internationaux. - Défis de l'heure d'été (DST) : Les transitions vers l'heure d'été peuvent faire que les jours durent 23 ou 25 heures. Une simple arithmétique (par exemple, ajouter 24 heures à une date) peut ne pas toujours donner le jour calendaire suivant, ce qui entraîne des calculs incorrects lorsqu'un "jour" est supposé être une période fixe de 24 heures.
Ces limitations ont historiquement contraint les développeurs à s'appuyer sur des bibliothèques tierces comme Moment.js ou date-fns, ou à écrire du code personnalisé complexe et sujet aux erreurs pour gérer correctement les intervalles de temps. Temporal vise à intégrer nativement ces capacités dans JavaScript.
Présentation de JavaScript Temporal : Une approche moderne du temps
L'API Temporal est un nouvel objet global complet en JavaScript, conçu pour remplacer l'ancien objet Date. Ses principes fondamentaux sont l'immuabilité, la gestion explicite des fuseaux horaires et une séparation claire des préoccupations entre les différents concepts temporels. Temporal introduit plusieurs nouvelles classes, chacune représentant un aspect distinct du temps :
Temporal.Instant: Un point spécifique et sans ambiguïté dans le temps, indépendant de tout calendrier ou fuseau horaire (similaire à un timestamp Unix, mais avec une précision à la nanoseconde).Temporal.ZonedDateTime: Un point spécifique dans le temps dans un calendrier et un fuseau horaire particuliers. C'est la représentation la plus complète d'une date et d'une heure spécifiques pour un utilisateur.Temporal.PlainDate: Une date de calendrier (année, mois, jour) sans heure ni fuseau horaire.Temporal.PlainTime: Une heure d'horloge (heure, minute, seconde, etc.) sans date ni fuseau horaire.Temporal.PlainDateTime: Une date de calendrier et une heure d'horloge combinées, sans fuseau horaire.Temporal.PlainYearMonth: Une année et un mois spécifiques dans un système de calendrier.Temporal.PlainMonthDay: Un mois et un jour spécifiques dans un système de calendrier.Temporal.Duration: Une durée de temps signée, telle que "5 heures et 30 minutes" ou "2 jours". C'est l'objet de notre attention dans ce guide.
Tous les objets Temporal sont immuables, ce qui signifie que des opérations comme l'ajout ou la soustraction de temps créent de nouveaux objets au lieu de modifier ceux qui existent, améliorant ainsi la prévisibilité et réduisant les bogues.
Comprendre Temporal.Duration
Un objet Temporal.Duration représente une durée. Fait crucial, il est indépendant d'un point de départ ou de fin spécifique. C'est simplement "combien de temps" s'est écoulé ou s'écoulera. Il peut être composé d'années, de mois, de semaines, de jours, d'heures, de minutes, de secondes, de millisecondes, de microsecondes et de nanosecondes. Chaque composant est un entier et peut être positif ou négatif.
Par exemple, "2 heures et 30 minutes" est une durée. "La période du 1er janvier au 1er mars" est une durée entre deux points spécifiques, qui peut être *représentée* par un Temporal.Duration, mais la Duration elle-même n'est que l'intervalle.
Créer une durée
Il existe plusieurs manières simples de créer des objets Temporal.Duration :
1. Utiliser le constructeur
Le constructeur vous permet de spécifier chaque composant directement. Notez que les arguments sont ordonnés de la plus grande unité (années) à la plus petite (nanosecondes).
// new Temporal.Duration(années, mois, semaines, jours, heures, minutes, secondes, millisecondes, microsecondes, nanosecondes)
// Une durée de 2 heures et 30 minutes
const duration1 = new Temporal.Duration(0, 0, 0, 0, 2, 30, 0, 0, 0, 0);
console.log(duration1.toString()); // P2H30M
// Une durée de 1 an, 2 mois, 3 jours
const duration2 = new Temporal.Duration(1, 2, 0, 3);
console.log(duration2.toString()); // P1Y2M3D
// Une durée de -5 jours
const duration3 = new Temporal.Duration(0, 0, 0, -5);
console.log(duration3.toString()); // P-5D
2. Utiliser Temporal.Duration.from() avec un objet
C'est souvent la manière la plus lisible de créer des durées, vous permettant de ne spécifier que les composants dont vous avez besoin.
// Durée de 1,5 heure
const halfHourDuration = Temporal.Duration.from({ hours: 1, minutes: 30 });
console.log(halfHourDuration.toString()); // P1H30M
// Durée de 7 jours (1 semaine)
const oneWeekDuration = Temporal.Duration.from({ days: 7 });
console.log(oneWeekDuration.toString()); // P7D
// Durée avec des secondes fractionnaires (ex: 2,5 secondes)
const twoPointFiveSeconds = Temporal.Duration.from({ seconds: 2, milliseconds: 500 });
console.log(twoPointFiveSeconds.toString()); // PT2.5S
// Durée négative
const negativeDuration = Temporal.Duration.from({ minutes: -45 });
console.log(negativeDuration.toString()); // PT-45M
3. Utiliser Temporal.Duration.from() avec une chaîne ISO 8601
Temporal s'appuie sur le format de durée ISO 8601, qui est une norme pour représenter les durées. C'est excellent pour analyser des durées provenant de sources de données externes.
Le format ressemble généralement à P[années]Y[mois]M[semaines]W[jours]DT[heures]H[minutes]M[secondes]S. Le T sépare les composants de date des composants de temps.
// 1 an, 2 mois, 3 jours
const isoDuration1 = Temporal.Duration.from('P1Y2M3D');
console.log(isoDuration1.toString()); // P1Y2M3D
// 4 heures, 5 minutes, 6 secondes
const isoDuration2 = Temporal.Duration.from('PT4H5M6S');
console.log(isoDuration2.toString()); // PT4H5M6S
// Une durée combinée
const isoDuration3 = Temporal.Duration.from('P7DT12H30M');
console.log(isoDuration3.toString()); // P7DT12H30M
// Les secondes fractionnaires sont également prises en charge
const isoDuration4 = Temporal.Duration.from('PT1.5S');
console.log(isoDuration4.toString()); // PT1.5S
Effectuer des calculs avec des durées
La véritable puissance de Temporal.Duration réside dans ses capacités arithmétiques. Vous pouvez additionner, soustraire, multiplier et diviser des durées, et aussi les ajouter/soustraire à d'autres types date/heure de Temporal. Toutes les opérations renvoient de nouveaux objets Temporal.Duration en raison de l'immuabilité.
Ajouter des durées
La méthode add() combine deux durées.
const sprintDuration = Temporal.Duration.from({ weeks: 2 });
const bufferDuration = Temporal.Duration.from({ days: 3 });
const totalProjectTime = sprintDuration.add(bufferDuration);
console.log(totalProjectTime.toString()); // P2W3D (ou P17D si normalisé plus tard)
// Ajouter une durée négative équivaut à une soustraction
const result = Temporal.Duration.from({ hours: 5 }).add({ hours: -2 });
console.log(result.toString()); // PT3H
Vous pouvez également ajouter une durée à n'importe quel objet date/heure de Temporal. C'est là que la magie opère, car Temporal gère correctement les changements de fuseau horaire et les transitions DST lorsque cela est pertinent.
const projectStart = Temporal.PlainDateTime.from('2023-10-26T09:00:00');
const projectDuration = Temporal.Duration.from({ days: 10, hours: 4 });
const projectEnd = projectStart.add(projectDuration);
console.log(projectEnd.toString()); // 2023-11-05T13:00:00
// Avec un ZonedDateTime, les règles de fuseau horaire sont appliquées correctement
const meetingStartUTC = Temporal.ZonedDateTime.from('2024-03-09T14:00:00[UTC]');
const meetingDuration = Temporal.Duration.from({ hours: 1, minutes: 45 });
const meetingEndUTC = meetingStartUTC.add(meetingDuration);
console.log(meetingEndUTC.toString()); // 2024-03-09T15:45:00+00:00[UTC]
// Exemple de franchissement d'une limite de l'heure d'été (en supposant que 'Europe/Berlin' change à 03:00 le 2024-03-31)
const springForwardStart = Temporal.ZonedDateTime.from('2024-03-30T22:00:00[Europe/Berlin]');
const twentyFourHours = Temporal.Duration.from({ hours: 24 });
const nextDay = springForwardStart.add(twentyFourHours); // Ajoute 24 heures réelles
console.log(springForwardStart.toString()); // 2024-03-30T22:00:00+01:00[Europe/Berlin]
console.log(nextDay.toString()); // 2024-03-31T23:00:00+02:00[Europe/Berlin] (L'heure locale a sauté une heure)
Remarquez comment l'ajout de 24 heures à 2024-03-30T22:00:00 à Berlin (qui est UTC+1) donne 2024-03-31T23:00:00 (maintenant UTC+2). L'horloge a avancé d'une heure, donc l'heure d'horloge locale est une heure plus tard à la même date par rapport à l'heure d'horloge de départ. Cela démontre précisément la prise en charge des fuseaux horaires et de l'heure d'été par Temporal lors de calculs sur un `ZonedDateTime`.
Soustraire des durées
La méthode subtract() fonctionne de manière similaire à add(), mais elle retire du temps.
const deadlineDuration = Temporal.Duration.from({ days: 30 });
const gracePeriod = Temporal.Duration.from({ days: 5 });
const effectiveDeadline = deadlineDuration.subtract(gracePeriod);
console.log(effectiveDeadline.toString()); // P25D
const taskEnd = Temporal.PlainDateTime.from('2023-12-01T17:00:00');
const taskDuration = Temporal.Duration.from({ hours: 8, minutes: 30 });
const taskStart = taskEnd.subtract(taskDuration);
console.log(taskStart.toString()); // 2023-12-01T08:30:00
Multiplier et diviser des durées
Les méthodes multiply() et divide() mettent à l'échelle les composants d'une durée par un facteur donné. C'est utile pour des scénarios comme le calcul du temps total pour plusieurs itérations d'une tâche.
const trainingSession = Temporal.Duration.from({ minutes: 45 });
const weeklyTraining = trainingSession.multiply(5); // Cinq sessions par semaine
console.log(weeklyTraining.toString()); // PT225M
const totalProjectHours = Temporal.Duration.from({ hours: 160 });
const teamMembers = 4;
const hoursPerMember = totalProjectHours.divide(teamMembers);
console.log(hoursPerMember.toString()); // PT40H
Inverser le signe des durées
La méthode negate() inverse le signe de tous les composants d'une durée. Une durée positive devient négative, et vice-versa.
const delayDuration = Temporal.Duration.from({ hours: 3 });
const advanceDuration = delayDuration.negate();
console.log(delayDuration.toString()); // PT3H
console.log(advanceDuration.toString()); // PT-3H
Valeur absolue des durées
La méthode abs() renvoie un nouveau Temporal.Duration avec tous les composants rendus positifs, vous donnant ainsi la magnitude de la durée quel que soit son signe.
const negativeDelay = Temporal.Duration.from({ minutes: -60 });
const positiveDuration = negativeDelay.abs();
console.log(negativeDelay.toString()); // PT-60M
console.log(positiveDuration.toString()); // PT60M
Comparer et normaliser les durées
La comparaison des durées peut être délicate, surtout lorsque différentes unités sont impliquées (par exemple, 1 mois est-il égal à 30 jours ?). Temporal fournit des outils pour la comparaison et la normalisation afin de gérer ces complexités.
Comparer des durées avec compare()
La méthode statique Temporal.Duration.compare(duration1, duration2, options) renvoie :
-1siduration1est infĂ©rieure Ăduration20siduration1est Ă©gale Ăduration21siduration1est supĂ©rieure Ăduration2
Crucialement, lors de la comparaison de durées qui incluent des unités de longueur variable comme les années, les mois ou les semaines, vous devez souvent fournir une option relativeTo. Ce paramètre est un objet `Temporal.ZonedDateTime` ou `Temporal.PlainDateTime` qui fournit un contexte sur la manière d'interpréter ces unités (par exemple, combien de jours il y a dans un mois ou une année spécifique).
const oneHour = Temporal.Duration.from({ hours: 1 });
const sixtyMinutes = Temporal.Duration.from({ minutes: 60 });
console.log(Temporal.Duration.compare(oneHour, sixtyMinutes)); // 0 (Elles sont équivalentes)
const oneMonth = Temporal.Duration.from({ months: 1 });
const thirtyDays = Temporal.Duration.from({ days: 30 });
// Sans relativeTo, les comparaisons de mois/années sont difficiles
console.log(Temporal.Duration.compare(oneMonth, thirtyDays)); // 0 (Temporal fait une supposition raisonnable sans contexte, souvent basée sur une moyenne)
// Avec relativeTo, la comparaison est précise en fonction du calendrier du contexte
const startOfJanuary = Temporal.PlainDate.from('2023-01-01');
const endOfFebruaryLeap = Temporal.PlainDate.from('2024-02-01'); // Année bissextile
// En janvier 2023, 1 mois équivaut à 31 jours
const comparisonJan = Temporal.Duration.compare(oneMonth, thirtyDays, { relativeTo: startOfJanuary });
console.log(`1 mois vs 30 jours en janv. 2023 : ${comparisonJan}`); // 1 (1 mois > 30 jours)
// En février 2024 (année bissextile), 1 mois équivaut à 29 jours
const comparisonFeb = Temporal.Duration.compare(oneMonth, thirtyDays, { relativeTo: endOfFebruaryLeap });
console.log(`1 mois vs 30 jours en fév. 2024 : ${comparisonFeb}`); // -1 (1 mois < 30 jours)
Normaliser les durées avec normalize() et round()
Les durées peuvent être représentées de nombreuses manières (par exemple, 90 minutes ou 1 heure et 30 minutes). La normalisation et l'arrondi aident à standardiser ces représentations pour la cohérence et l'affichage.
normalize()
La méthode normalize() simplifie les composants de la durée lorsque c'est possible (par exemple, 60 minutes deviennent 1 heure, 24 heures deviennent 1 jour, à condition que le contexte `relativeTo` le permette si des mois/années sont impliqués).
const longMinutes = Temporal.Duration.from({ minutes: 90 });
console.log(longMinutes.toString()); // PT90M
console.log(longMinutes.normalize().toString()); // PT1H30M
const multipleDays = Temporal.Duration.from({ hours: 48 });
console.log(multipleDays.toString()); // PT48H
console.log(multipleDays.normalize().toString()); // P2D
round()
La méthode round() est plus puissante pour transformer et arrondir les durées à des unités spécifiques. Elle prend un objet d'options avec :
largestUnit: La plus grande unité à inclure dans le résultat (par exemple, 'years', 'days', 'hours').smallestUnit: La plus petite unité à inclure dans le résultat (par exemple, 'minutes', 'seconds', 'milliseconds').roundingIncrement: Un entier par lequel arrondir la plus petite unité (par exemple, 5 pour arrondir aux 5 minutes les plus proches).roundingMode: Comment gérer les cas d'égalité (par exemple, 'halfExpand', 'trunc', 'ceil', 'floor').relativeTo: Requis pour arrondir les durées contenant des années, des mois ou des semaines.
const complexDuration = Temporal.Duration.from({ hours: 2, minutes: 45, seconds: 30 });
// Arrondir Ă l'heure la plus proche
const roundedToHours = complexDuration.round({ smallestUnit: 'hour' });
console.log(roundedToHours.toString()); // PT3H
// Arrondir aux 30 minutes les plus proches, en conservant les heures
const roundedTo30Minutes = complexDuration.round({
largestUnit: 'hour',
smallestUnit: 'minute',
roundingIncrement: 30
});
console.log(roundedTo30Minutes.toString()); // PT3H
const preciseDuration = Temporal.Duration.from({ minutes: 123, seconds: 45 });
// Afficher en heures et minutes
const formattedDuration = preciseDuration.round({ largestUnit: 'hour', smallestUnit: 'minute' });
console.log(formattedDuration.toString()); // PT2H4M
// L'arrondi avec des mois/années nécessite relativeTo
const longTermDuration = Temporal.Duration.from({ months: 1, days: 10 });
const referenceDate = Temporal.PlainDate.from('2023-01-15');
// Arrondir aux mois, par rapport Ă une date
const roundedToMonths = longTermDuration.round({ largestUnit: 'month', smallestUnit: 'month', relativeTo: referenceDate });
console.log(roundedToMonths.toString()); // P1M
Calculer des durées entre des objets Temporal
L'une des utilisations les plus fréquentes des durées est le calcul de l'intervalle de temps entre deux points spécifiques dans le temps. Temporal fournit les méthodes until() et since() sur ses objets date-heure à cet effet.
Méthode until()
La méthode until() calcule la durée de l'objet récepteur à l'objet en argument. Elle est inclusive du point de départ et exclusive du point de fin. Elle prend un objet d'options similaire à round() pour spécifier les unités souhaitées et le comportement d'arrondi.
const startDate = Temporal.PlainDate.from('2023-01-01');
const endDate = Temporal.PlainDate.from('2023-03-15');
// Durée dans les plus grandes unités possibles (mois, puis jours)
const projectLength = startDate.until(endDate);
console.log(projectLength.toString()); // P2M14D
// Durée uniquement en jours
const totalDays = startDate.until(endDate, { largestUnit: 'day' });
console.log(totalDays.toString()); // P73D
// Durée entre deux heures spécifiques, en respectant les fuseaux horaires
const meetingStart = Temporal.ZonedDateTime.from('2024-07-20T10:00:00[America/New_York]');
const meetingEnd = Temporal.ZonedDateTime.from('2024-07-20T11:30:00[America/New_York]');
const elapsedMeetingTime = meetingStart.until(meetingEnd, { largestUnit: 'hour', smallestUnit: 'minute' });
console.log(elapsedMeetingTime.toString()); // PT1H30M
// Durée entre fuseaux horaires (de NYC à Londres)
const nyStartTime = Temporal.ZonedDateTime.from('2024-08-01T09:00:00[America/New_York]');
const londonEndTime = Temporal.ZonedDateTime.from('2024-08-01T17:00:00[Europe/London]');
const travelDuration = nyStartTime.until(londonEndTime);
console.log(travelDuration.toString()); // PT13H (Temps réel écoulé, pas la différence d'heure d'horloge)
Le dernier exemple est particulièrement instructif. Même si New York a 5 heures de retard sur Londres, et que les heures d'horloge sont 9h et 17h le même jour, la méthode until() calcule correctement le temps réel écoulé de 13 heures. C'est parce que ZonedDateTime gère implicitement la différence de fuseau horaire.
Méthode since()
La méthode since() est l'inverse de until(). Elle calcule la durée de l'objet en argument à l'objet récepteur, ce qui donne une durée négative si l'argument est dans le futur par rapport au récepteur.
const currentDateTime = Temporal.ZonedDateTime.from('2024-06-15T12:00:00[Europe/Paris]');
const historicEvent = Temporal.ZonedDateTime.from('2024-01-01T00:00:00[Europe/Paris]');
const timeSinceEvent = currentDateTime.since(historicEvent, { largestUnit: 'month', smallestUnit: 'day' });
console.log(timeSinceEvent.toString()); // P5M14D
const futureDate = Temporal.PlainDate.from('2025-01-01');
const pastDate = Temporal.PlainDate.from('2024-01-01');
const durationFromFuture = pastDate.since(futureDate);
console.log(durationFromFuture.toString()); // P-1Y
Gérer différentes unités et l'arrondi pour les durées calculées
Lors du calcul des durées, il est souvent nécessaire de spécifier `largestUnit` et `smallestUnit` pour obtenir une représentation lisible par l'homme, en particulier pour l'âge, le temps écoulé ou les comptes à rebours.
const birthDate = Temporal.PlainDate.from('1990-07-15');
const today = Temporal.PlainDate.from('2024-06-15');
// Calculer l'âge en années, mois et jours
const age = birthDate.until(today, { largestUnit: 'year', smallestUnit: 'day' });
console.log(`Âge : ${age.years} ans, ${age.months} mois, ${age.days} jours`); // Âge : 33 ans, 11 mois, 0 jours
// Calculer le temps restant pour une tâche en heures et minutes
const now = Temporal.Instant.fromEpochSeconds(Date.now() / 1000);
const deadline = Temporal.Instant.from('2024-07-01T09:00:00Z');
const timeLeft = now.until(deadline, { largestUnit: 'hour', smallestUnit: 'minute', roundingMode: 'ceil' });
console.log(`Temps restant : ${timeLeft.hours} heures et ${timeLeft.minutes} minutes.`); // Exemple : Temps restant : 355 heures et 38 minutes.
Formater les durées pour un public international
Bien que Temporal.Duration fournisse des représentations programmatiques précises des intervalles de temps, il n'a pas de méthode toLocaleString() intégrée. C'est intentionnel : les durées sont des longueurs de temps abstraites, et leur affichage peut varier considérablement en fonction du contexte, de la locale et du niveau de détail souhaité. C'est à vous, en tant que développeur, de présenter les durées d'une manière conviviale et compréhensible à l'échelle mondiale.
Représentation en chaîne ISO 8601
La méthode toString() par défaut d'un objet Temporal.Duration renvoie sa représentation en chaîne ISO 8601. C'est excellent pour la communication de machine à machine, la sérialisation et le stockage, mais rarement pour un affichage direct aux utilisateurs finaux.
const examDuration = Temporal.Duration.from({ hours: 2, minutes: 15 });
console.log(examDuration.toString()); // PT2H15M
const holidayDuration = Temporal.Duration.from({ weeks: 2, days: 3 });
console.log(holidayDuration.toString()); // P2W3D
Formatage manuel pour la lisibilité et l'internationalisation
Pour l'affichage destiné aux utilisateurs, vous extrairez généralement les composants d'une durée et les formaterez en utilisant l'interpolation de chaînes et l'API Intl de JavaScript.
Voici un exemple de fonction personnalisée qui formate une durée :
function formaterDureeLisible(duration, locale = 'fr-FR') {
const parts = [];
// Utilisation de Intl.NumberFormat pour un formatage des nombres adapté à la locale
const numberFormatter = new Intl.NumberFormat(locale);
if (duration.years !== 0) {
parts.push(numberFormatter.format(duration.years) + ' ' + (Math.abs(duration.years) > 1 ? 'ans' : 'an'));
}
if (duration.months !== 0) {
parts.push(numberFormatter.format(duration.months) + ' mois');
}
if (duration.weeks !== 0) {
parts.push(numberFormatter.format(duration.weeks) + ' ' + (Math.abs(duration.weeks) > 1 ? 'semaines' : 'semaine'));
}
if (duration.days !== 0) {
parts.push(numberFormatter.format(duration.days) + ' ' + (Math.abs(duration.days) > 1 ? 'jours' : 'jour'));
}
if (duration.hours !== 0) {
parts.push(numberFormatter.format(duration.hours) + ' ' + (Math.abs(duration.hours) > 1 ? 'heures' : 'heure'));
}
if (duration.minutes !== 0) {
parts.push(numberFormatter.format(duration.minutes) + ' ' + (Math.abs(duration.minutes) > 1 ? 'minutes' : 'minute'));
}
if (duration.seconds !== 0) {
// Arrondir les secondes pour l'affichage si elles ont des parties fractionnaires
const roundedSeconds = numberFormatter.format(duration.seconds.toFixed(0)); // Ou toFixed(1) pour une décimale
parts.push(roundedSeconds + ' ' + (Math.abs(duration.seconds) > 1 ? 'secondes' : 'seconde'));
}
if (parts.length === 0) {
// Gérer les cas où la durée est nulle ou très petite (ex: nanosecondes uniquement)
if (duration.milliseconds !== 0 || duration.microseconds !== 0 || duration.nanoseconds !== 0) {
const totalMs = duration.milliseconds + duration.microseconds / 1000 + duration.nanoseconds / 1_000_000;
return numberFormatter.format(totalMs.toFixed(2)) + ' millisecondes';
}
return '0 secondes';
}
// Joindre les parties avec une virgule et 'et' pour la dernière partie (logique de base en français)
if (parts.length > 1) {
const lastPart = parts.pop();
return parts.join(', ') + ' et ' + lastPart;
} else {
return parts[0];
}
}
const tripDuration = Temporal.Duration.from({ days: 3, hours: 10, minutes: 45 });
console.log(formaterDureeLisible(tripDuration, 'fr-FR')); // 3 jours, 10 heures et 45 minutes
console.log(formaterDureeLisible(tripDuration, 'de-DE')); // 3 Tage, 10 Stunden und 45 Minuten (exemple de Intl.NumberFormat)
const meetingReminder = Temporal.Duration.from({ minutes: 5 });
console.log(formaterDureeLisible(meetingReminder, 'fr-FR')); // 5 minutes
const microDuration = Temporal.Duration.from({ nanoseconds: 1234567 });
console.log(formaterDureeLisible(microDuration, 'fr-FR')); // 1,23 millisecondes
Pour une gestion plus avancée des pluriels et du formatage de listes localisées, vous pourriez combiner cela avec Intl.RelativeTimeFormat ou des bibliothèques de templating de chaînes plus complexes. La clé est de séparer le calcul de la durée (géré par Temporal) de sa présentation (gérée par votre logique de formatage et Intl).
Utiliser Intl.DurationFormat (Futur/Proposition)
Il existe une proposition TC39 en cours pour Intl.DurationFormat, qui vise à fournir un moyen natif et adapté à la locale pour formater les durées. Si elle est standardisée et implémentée, elle simplifierait grandement le formatage manuel montré ci-dessus, offrant une solution robuste pour l'internationalisation directement au sein de l'écosystème JavaScript.
Elle fonctionnerait probablement de manière similaire aux autres objets Intl :
// Ceci est hypothétique et basé sur l'état actuel de la proposition
// Vérifiez la compatibilité des navigateurs avant de l'utiliser en production
/*
const duration = Temporal.Duration.from({ days: 1, hours: 2, minutes: 30 });
const formatter = new Intl.DurationFormat('fr-FR', {
style: 'long',
years: 'long',
days: 'long',
hours: 'long',
minutes: 'long',
});
console.log(formatter.format(duration)); // "1 jour, 2 heures, 30 minutes"
const shortFormatter = new Intl.DurationFormat('fr-FR', { style: 'short', hours: 'numeric', minutes: 'numeric' });
console.log(shortFormatter.format(duration)); // "1 j, 2 h, 30 min"
*/
Bien qu'elle ne soit pas encore disponible dans tous les environnements, gardez un œil sur cette proposition car elle promet d'être la solution définitive pour le formatage global des durées à l'avenir.
Cas d'utilisation pratiques et considérations mondiales
Temporal.Duration n'est pas seulement une amélioration académique ; il résout des problèmes du monde réel dans des applications qui opèrent à travers différents fuseaux horaires et cultures.
1. Planification et gestion d'événements
Pour les plateformes gérant des événements, des conférences ou des rendez-vous internationaux, le calcul de la durée des événements, du temps restant avant un événement ou de la durée d'une pause est essentiel. Temporal.Duration garantit que ces calculs sont précis, quel que soit l'emplacement de l'utilisateur ou les règles de son fuseau horaire local.
// Calculer le temps restant pour un webinaire mondial
const now = Temporal.ZonedDateTime.from('2024-07-25T10:00:00[Europe/London]'); // Heure actuelle d'exemple
const webinarStart = Temporal.ZonedDateTime.from('2024-07-26T14:30:00[Asia/Tokyo]');
const timeUntilWebinar = now.until(webinarStart, {
largestUnit: 'hour',
smallestUnit: 'minute',
roundingMode: 'ceil' // Arrondir au supérieur pour s'assurer que les utilisateurs ne le manquent pas
});
console.log(`Le webinaire commence dans ${timeUntilWebinar.hours} heures et ${timeUntilWebinar.minutes} minutes.`);
// Le résultat sera précis en fonction du temps réel écoulé entre ces deux ZonedDateTimes.
2. Métriques de performance et journalisation
La mesure du temps d'exécution des opérations, des temps de réponse des API ou de la durée des tâches par lots nécessite une haute précision. Temporal.Duration, surtout lorsqu'il est combiné avec Temporal.Instant (qui offre une précision à la nanoseconde), est idéal pour cela.
const startTime = Temporal.Instant.now();
// Simuler une opération complexe
for (let i = 0; i < 1_000_000; i++) { Math.sqrt(i); }
const endTime = Temporal.Instant.now();
const executionDuration = startTime.until(endTime);
// Formater en secondes avec des millisecondes pour l'affichage
const formattedExecution = executionDuration.round({ smallestUnit: 'millisecond', largestUnit: 'second' });
console.log(`L'opération a pris ${formattedExecution.seconds},${String(formattedExecution.milliseconds).padStart(3, '0')} secondes.`);
3. Calculs financiers
En finance, des intervalles de temps précis sont primordiaux pour calculer les intérêts, les durées de prêt ou les périodes d'investissement. Le nombre exact de jours, de mois ou d'années dans une période doit être précis, en particulier lorsqu'il s'agit d'années bissextiles ou de longueurs de mois spécifiques.
const loanStartDate = Temporal.PlainDate.from('2023-04-01');
const loanEndDate = Temporal.PlainDate.from('2028-03-31');
const loanTerm = loanStartDate.until(loanEndDate, { largestUnit: 'year', smallestUnit: 'month' });
console.log(`Durée du prêt : ${loanTerm.years} ans et ${loanTerm.months} mois.`); // 4 ans et 11 mois
4. Défis de l'internationalisation revisités
-
Fuseaux horaires et heure d'été :
Temporal.Durationlui-même est indépendant des fuseaux horaires ; il représente une durée fixe. Cependant, lorsque vous ajoutez ou soustrayez uneDurationà /d'unZonedDateTime, Temporal applique correctement les règles de fuseau horaire, y compris les changements d'heure d'été. Cela garantit qu'une "durée de 24 heures" avance réellement unZonedDateTimede 24 heures réelles, même si cela signifie franchir une limite DST qui rend la journée *d'horloge* plus courte ou plus longue. Cette cohérence est un avantage majeur par rapport à `Date`. -
Variations culturelles : Différentes cultures expriment les durées différemment. Bien que
Temporal.Durationfournisse les composants bruts (années, mois, jours, etc.), il est de votre responsabilité d'utiliser les API `Intl` ou une logique personnalisée pour les présenter d'une manière qui soit pertinente pour la locale de l'utilisateur. Par exemple, certaines cultures pourraient exprimer "1 heure et 30 minutes" par "90 minutes" ou utiliser des règles de pluralisation différentes. -
Systèmes de calendrier : Temporal prend également en charge différents systèmes de calendrier (par exemple, les calendriers japonais, persan, islamique). Bien que
Durationsoit indépendant du calendrier, lorsqu'il interagit avec des types qui en tiennent compte commePlainDateouZonedDateTime, l'arithmétique respecte les règles spécifiques de ce calendrier (par exemple, le nombre de jours dans un mois, les règles des années bissextiles dans ce calendrier). C'est crucial pour les applications mondiales qui pourraient avoir besoin d'afficher des dates dans des calendriers non grégoriens.
Statut actuel et adoption de Temporal
Fin 2023/début 2024, l'API JavaScript Temporal est une proposition TC39 de stade 3. Cela signifie que la spécification est largement stable, et qu'elle est en cours d'implémentation et de test dans divers moteurs JavaScript et navigateurs. Les principaux navigateurs comme Chrome, Firefox et Safari travaillent activement à l'implémentation de Temporal, avec des drapeaux expérimentaux ou des versions préliminaires déjà disponibles.
Bien qu'elle ne soit pas encore universellement disponible sans un polyfill, son stade avancé indique qu'elle deviendra très probablement une partie standard de JavaScript. Les développeurs peuvent commencer à expérimenter avec Temporal dès aujourd'hui en utilisant des polyfills ou en activant des fonctionnalités expérimentales dans leurs environnements de développement de navigateur. Une adoption précoce vous permet de prendre de l'avance, de comprendre ses avantages et de l'intégrer dans vos projets au fur et à mesure que le support des navigateurs se déploie.
Son adoption généralisée future réduira considérablement le besoin de bibliothèques de date/heure externes, en fournissant une solution native et robuste pour la gestion du temps en JavaScript.
Meilleures pratiques pour l'utilisation de Temporal.Duration
Pour maximiser les avantages de Temporal.Duration dans vos applications, considérez ces meilleures pratiques :
-
Privilégiez
Temporal.Duration.from(): Lors de la création de durées à partir de composants connus ou de chaînes ISO,Temporal.Duration.from()avec un objet littéral est souvent plus lisible et moins sujet aux erreurs que les arguments positionnels du constructeur. -
Utilisez
relativeTopour les comparaisons ambiguës : Fournissez toujours une optionrelativeTolorsque vous comparez ou arrondissez des durées qui contiennent des années, des mois ou des semaines. Cela garantit des calculs précis en fournissant le contexte calendaire nécessaire. -
Exploitez
until()etsince(): Pour calculer l'intervalle entre deux points spécifiques dans le temps, préférez les méthodesuntil()etsince()sur les objets date-heure de Temporal. Elles gèrent correctement les complexités des fuseaux horaires et de l'heure d'été. -
Normalisez et arrondissez pour l'affichage : Avant de présenter des durées aux utilisateurs, envisagez d'utiliser
normalize()et surtoutround()pour convertir la durée dans les unités les plus appropriées et compréhensibles (par exemple, convertir 90 minutes en "1 heure et 30 minutes"). -
Séparez la représentation interne de l'affichage : Gardez vos calculs de durée internes précis avec
Temporal.Duration. Ne transformez et ne formatez pour l'affichage qu'au moment du rendu de l'interface utilisateur, en utilisant des fonctions de formatage personnalisées et l'APIIntlpour une précision globale. - Restez à jour : Gardez un œil sur les progrès de l'API Temporal et la compatibilité des navigateurs. À mesure qu'elle se rapproche de la standardisation complète, l'écosystème évoluera, et de nouveaux outils ou meilleures pratiques pourraient émerger.
- Soyez attentif à la précision : Bien que Temporal prenne en charge la précision à la nanoseconde, n'utilisez que la précision dont vous avez réellement besoin. Une précision plus élevée peut parfois rendre le débogage plus difficile ou entraîner des arrondis inattendus lors de la conversion vers des affichages de moindre précision.
Conclusion
L'introduction de Temporal.Duration marque un bond en avant significatif pour les développeurs JavaScript aux prises avec l'arithmétique des intervalles de temps. En fournissant une API immuable, explicite et adaptée au contexte mondial, Temporal répond aux limitations de longue date de l'ancien objet Date.
Vous pouvez désormais effectuer en toute confiance des calculs de temps complexes, mesurer avec précision des périodes et présenter des durées d'une manière à la fois précise et culturellement appropriée pour les utilisateurs du monde entier. Que vous calculiez la durée d'un cycle de publication de logiciel, le temps restant avant le lancement d'un produit mondial ou l'âge exact d'une personne, Temporal.Duration offre les outils dont vous avez besoin pour créer des applications robustes et fiables.
Adoptez Temporal.Duration et transformez la façon dont vous gérez le temps dans vos projets JavaScript. L'avenir de la gestion de la date et de l'heure en JavaScript est là , promettant une expérience de développement plus prévisible, puissante et globalement compatible.